package com.supor.aiot.common.helper;

import android.content.Context;
import android.text.TextUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.android.baseconfig.base.RxBus3;
import com.android.baseconfig.common.utils.GsonUtil;
import com.android.baseconfig.common.utils.ThreadUtils;
import com.android.baseconfig.common.utils.ToastUtils;
import com.supor.aiot.common.app.AppAgent;
import com.supor.aiot.common.app.Constants;
import com.supor.aiot.common.app.SuporPresenter;
import com.supor.aiot.common.entity.DeviceInfo;
import com.supor.aiot.common.entity.DeviceStatus;
import com.supor.aiot.common.entity.DeviceTree;
import com.supor.aiot.websocket.ConnectCallback;
import com.supor.aiot.websocket.ErrorMessage;
import com.supor.aiot.websocket.Logc;
import com.supor.aiot.websocket.OnReceiveMessageListener;
import com.supor.aiot.websocket.WsManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * ---------------------------------------------------------------- <br>
 * Copyright (C) 2014-2021, by het, ShenZhen, All rights reserved.  <br>
 * ---------------------------------------------------------------- <br>
 * <p>
 * 机智云websocket操作步骤： <br>
 * 1、连接机智云websocket； <br>
 * 2、连接成功后执行登录操作； <br>
 * 3、登录成功后订阅设备状态消息； <br>
 * <p>
 * 描述: WsHelper <br>
 * 作者: lei <br>
 * 日期: 2021/9/22 <br>
 */
public class WsHelper implements OnReceiveMessageListener {

    private static WsHelper mInstance;
    private static String TAG = "websocket";
    private Context context;
    private String loginStr = null;
    private String heardBeatStr = null;
    ScheduledFuture future = null;
    /**
     * 是否退出登录
     */
    private volatile boolean isExit;
    private volatile boolean firstInit;
    private DeviceTree deviceTree;
    /**
     * websocket地址
     */
    private String host = Constants.WEBSOCKET_RELEASE_HOST;
    /**
     * 机智云APPID
     */
    private String gizwitsAppid = Constants.App.GIZWITS_RELEASE_APPID;
    private HashMap<String, DeviceStatus> deviceStatus = new HashMap<>();

    private WsHelper() {

    }

    public static WsHelper getInstance() {
        if (mInstance == null) {
            synchronized (WsHelper.class) {
                if (mInstance == null) {
                    mInstance = new WsHelper();
                }
            }
        }
        return mInstance;
    }

    private ConnectCallback connectCallback = new ConnectCallback() {
        @Override
        public void onSuccess(String msg) {
            Logc.d(TAG, "webSocket连接成功: " + msg);
            // Logc.i(TAG, "连接成功后发送一次心跳包");
            // WsHelper.getInstance().sendHeartPackage();
            if (deviceTree != null && !firstInit) {
                login();
            }
            firstInit = false;
        }

        @Override
        public void onError(ErrorMessage error) {
            if (error.getException() != null) {
                Logc.i(TAG, "webSocket连接失败: " + error.getException());
            } else {
                Logc.i(TAG, "webSocket连接失败: " + error.getMessage() + ", " + error.getCode());
            }
            if (!isExit) {
                // code: 1011, reason: , remote: true
                // 被服务器断开了，这种情况重新初始化
                if (error.getCode() == 1011) {
                    init(context, true);
                } else {
                    ThreadUtils.execute(() -> reconnectWs(), 5000);
                }
            }
        }
    };

    public void init(Context context) {
        init(context, false);
    }

    public void init(Context context, boolean forcedInit) {
        init(context, forcedInit, connectCallback);
    }

    /**
     * 是否初始化成功
     */
    private boolean initOk;

    /**
     * 初始化
     *
     * @param context
     * @param forcedInit      是否必须重新初始化  如果为false就只初始化一次
     * @param connectCallback
     */
    public void init(final Context context, final boolean forcedInit,
                     final ConnectCallback connectCallback) {
        if (initOk && !forcedInit) {
            return;
        }
        isExit = false;
        firstInit = true;
        this.context = context;
        // websocket当前地址
        if (AppAgent.currentServer == 0) {
            host = Constants.WEBSOCKET_RELEASE_HOST;
            gizwitsAppid = Constants.App.GIZWITS_RELEASE_APPID;
        } else if (AppAgent.currentServer == 1) {
            host = Constants.WEBSOCKET_RELEASE_HOST;
            gizwitsAppid = Constants.App.GIZWITS_RELEASE_APPID;
        } else if (AppAgent.currentServer == 2) {
            host = Constants.WEBSOCKET_TEST_HOST;
            gizwitsAppid = Constants.App.GIZWITS_TEST_APPID;
        }
        WsManager.getInstance().init(context, host);
        WsManager.getInstance().connect(new ConnectCallback() {
            @Override
            public void onSuccess(String userId) {
                initOk = true;
                WsManager.getInstance().setOnReceiveMessageListener(WsHelper.this);
                if (connectCallback != null) {
                    connectCallback.onSuccess(userId);
                }
            }

            @Override
            public void onError(ErrorMessage errorMessage) {
                initOk = false;
                if (connectCallback != null) {
                    connectCallback.onError(errorMessage);
                }
            }
        });
    }

    /**
     * 重连websocket
     */
    public void reConnectionWebsocket() throws Exception {
        WsManager.getInstance().reConnectionWebsocket();
    }

    /**
     * client 是否为空
     */
    public boolean isNullConnectionWebsocket() {
        return WsManager.getInstance().isNullReconnectionWebsocket();
    }

    /**
     * 判断client是否为关闭状态
     */
    public boolean isCloseConnectionWebsocket() {
        return WsManager.getInstance().isCloseConnectionWebsocket();
    }

    public void setDeviceTree(DeviceTree deviceTree) {
        this.deviceTree = deviceTree;
        login();
    }

    int connCount;

    @Override
    public void onReceived(String msg) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(msg);
            String value = (String) jsonObject.get(Constants.Key.CMD);
            if (value == null) {
                Logc.e(TAG, "invalid msg");
                return;
            }
            JSONObject dataObject;
            switch (value) {
                case Constants.Value.PONG:
                    Logc.i(TAG, "ws收到心跳包回复");
                    break;
                case Constants.Value.LOGIN_RES:
                    Logc.i(TAG, "ws收到登录回复");
                    dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
                    boolean isSuccess = (boolean) dataObject.get(Constants.Key.SUCCESS);
                    if (isSuccess) {
                        Logc.i(TAG, "ws登录成功");
                        ThreadUtils.execute(heartBeatRunnable, 1000);
                        subscribeDevice(deviceTree.device);
                    } else {
                        Logc.i(TAG, "ws登录失败，重新发送登录消息");
                        login();
                    }
                    break;
                case Constants.Value.SUBSCRIBE_RES:
                    Logc.i(TAG, "ws收到订阅设备回复");
                    dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
                    processSubscribeDevice(dataObject);
                    break;
                case Constants.Value.S_2_C_NOTI:
                    // Logc.d(TAG, "ws收到snoti通知");
                    processSNoti(jsonObject);
                    break;
                case Constants.Value.S_2_C_ACK:
                    Logc.i(TAG, "ws收到s2c_ack应答");
                    String did = jsonObject.getString(Constants.Key.DID);
                    String resSn = jsonObject.getString(Constants.Key.RES_SN);
                    Logc.d(TAG, "s2c_ack, res_sn: " + resSn + ", did: " + did);
                    break;
                case Constants.Value.S_2_C_ONLINE_STATUS:
                    Logc.i(TAG, "ws收到设备上下线通知");
                    dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
                    processOnlineStatus(dataObject);
                    break;
                case Constants.Value.S_2_C_BINDING_CHANGED:
                    Logc.i(TAG, "ws收到设备绑定解绑状态变更通知");
                    dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
                    processBindingStatus(dataObject);
                    break;
                case Constants.Value.S_2_C_INVALID_MSG:
                    Logc.i(TAG, "ws收到非法消息");
                    dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
                    processInvalidMsg(dataObject);
                    break;
                default:
                    break;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void processSNoti(JSONObject jsonObject) {
        String did = null;
        try {
            JSONObject dataObject = jsonObject.getJSONObject(Constants.Key.DATA);
            did = dataObject.getString(Constants.Key.DID);
            String mac = null;
            // 获取mac
            for (DeviceStatus status : deviceStatus.values()) {
                if (did.equalsIgnoreCase(status.did)) {
                    mac = status.mac;
                    Logc.i(TAG, "mac: " + mac + ", did: " + did);
                    break;
                }
            }
            if (TextUtils.isEmpty(mac)) {
                Logc.d(TAG, "processSNoti 没有找到设备mac");
                return;
            }
            // SuporPresenter.getInstance().reportCmd(mac, did,
            //         dataObject.getJSONObject(Constants.Key.ATTRS).toString());
            RxBus3.getInstance().post(Constants.Tag.SNOTI, dataObject);
        } catch (Exception e) {
            e.printStackTrace();
        }

        int resSn = jsonObject.getIntValue(Constants.Key.RES_SN);
        // Logc.e(TAG, "s2c_ack, res_sn: " + resSn + ", did: " + did);
        if (resSn != 0) {
            c2sAck(resSn, did);
        }
    }

    /**
     * 处理设备绑定解绑状态变更通知
     *
     * @param dataObject
     */
    private void processBindingStatus(JSONObject dataObject) {
        try {
            String did = dataObject.getString(Constants.Key.DID);
            boolean bind = dataObject.getBoolean(Constants.Key.BIND);
            DeviceStatus status = deviceStatus.get(did);
            if (status != null) {
                if (!bind) {
                    for (DeviceInfo info : deviceTree.device) {
                        if (did.equalsIgnoreCase(info.did)) {
                            deviceTree.device.remove(info);
                            break;
                        }
                    }
                    deviceStatus.remove(did);
                    SuporPresenter.getInstance().reportBindStatus(2, status.mac, status.did);
                    RxBus3.getInstance().post(Constants.Tag.BINDING_STATUS,
                            Constants.Value.SUCCESS);
                }
                return;
            }
            // TODO: 2021/9/23 shebeilieb
            GlobalDataCache.getBindDevice();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理设备上下线通知
     *
     * @param dataObject
     */
    private void processOnlineStatus(JSONObject dataObject) {
        try {
            String did = dataObject.getString(Constants.Key.DID);
            String mac = dataObject.getString(Constants.Key.MAC);
            boolean online = dataObject.getBoolean(Constants.Key.ONLINE);
            DeviceStatus status = deviceStatus.get(did);
            if (status != null) {
                status.onOffStatus = online;
                if (status.deviceInfo != null) {
                    status.deviceInfo.status = online;
                    status.mac = mac;
                }
                SuporPresenter.getInstance().reportOnline(status.mac, status.did,
                        status.onOffStatus ? 1 : 2);
                RxBus3.getInstance().post(Constants.Tag.ONLINE_STATUS, status.did);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理设备订阅消息，维护成功订阅的设备列表
     *
     * @param dataObject
     */
    private void processSubscribeDevice(JSONObject dataObject) {
        try {
            JSONArray successArray = dataObject.getJSONArray(Constants.Key.SUCCESS);
            for (int i = 0; i < successArray.size(); i++) {
                JSONObject object = successArray.getJSONObject(i);
                DeviceStatus status = new DeviceStatus();
                status.did = object.getString(Constants.Key.DID);
                for (DeviceInfo info : deviceTree.device) {
                    if (status.did.equalsIgnoreCase(info.did)) {
                        status.deviceInfo = info;
                        status.onOffStatus = info.onoffStatus;
                        status.mac = info.mac;
                        break;
                    }
                }
                deviceStatus.put(status.did, status);
            }
            // 订阅设备成功后上报一次设备列表
            String device = GsonUtil.getInstance().toJson(deviceTree);
            SuporPresenter.getInstance().reportDeviceTree(device);

            JSONArray failedArray = (JSONArray) dataObject.get(Constants.Key.FAILED);
            if (failedArray.size() == 0) {
                Logc.i(TAG, "ws订阅设备成功");
            } else {
                Logc.d(TAG, "ws订阅设备失败: " + failedArray.toString());
                List<DeviceInfo> deviceList = new ArrayList<>();
                for (int i = 0; i < failedArray.size(); i++) {
                    JSONObject object = failedArray.getJSONObject(i);
                    int errorCode = object.getIntValue(Constants.Key.ERROR_CODE);
                    if (errorCode == 1004) {
                        continue;
                    }
                    DeviceInfo info = new DeviceInfo();
                    info.did = object.getString(Constants.Key.DID);
                    deviceList.add(info);
                }
                subscribeDevice(deviceList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void processInvalidMsg(JSONObject dataObject) {
        try {
            int errorCode = dataObject.getIntValue(Constants.Key.ERROR_CODE);
            // You are already login! 登录后只订阅消息
            if (errorCode == 1012) {
                subscribeDevice(deviceTree.device);
            }
            String msg = dataObject.getString(Constants.Key.MSG);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户登陆
     * {
     * "cmd": "login_req",
     * "data":
     * {
     * "appid": <str>,
     * "uid": <str>,
     * "token": <str>,
     * "p0_type": "attrs_v4"|"custom", ("attrs_v4"
     * 指通过标准数据点协议的方式和云端交互，见下文“标准数据点操作”)
     * "heartbeat_interval": <int>, (心跳的时间间隔，单位为秒，值必须小于等于180)
     * "auto_subscribe": true | false
     * (true表示登陆成功后，服务器自动订阅所有已绑定设备；false则需要通过下面的“用户定阅设备消息”
     * 协议选择订阅设备，默认值为true。推荐设为false，然后按需定阅设备以节省开销)
     * }
     * }
     */
    public void login() {
        if (deviceTree == null || deviceTree.device == null
                || deviceTree.device.size() == 0
                || TextUtils.isEmpty(deviceTree.thirdUid)) {
            Logc.i(TAG, "没有找到设备");
            return;
        }
        if (TextUtils.isEmpty(loginStr)) {
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put(Constants.Key.CMD, Constants.Value.LOGIN_REQ);
                JSONObject dataObject = new JSONObject();
                dataObject.put(Constants.Key.APPID, gizwitsAppid);
                dataObject.put(Constants.Key.UID, deviceTree.thirdUid);
                dataObject.put(Constants.Key.TOKEN, deviceTree.thirdToken);
                dataObject.put(Constants.Key.P_0_TYPE, Constants.Value.ATTRS_V_4);
                dataObject.put(Constants.Key.HEARTBEAT_INTERVAL, HEART_BEAT_RATE);
                dataObject.put(Constants.Key.AUTO_SUBSCRIBE, false);
                jsonObject.put(Constants.Key.DATA, dataObject);
                loginStr = jsonObject.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (WsManager.getInstance().isNullReconnectionWebsocket()) {
            Logc.e(TAG, "client is null, wait");
        }
        if (!initOk) {
            ThreadUtils.execute(this::login, 500);
            return;
        }
        sendMessage(loginStr);
    }

    /**
     * 订阅设备状态
     * {
     * "cmd": "subscribe_req",
     * "data":
     * [
     * {"did": <str> },（需要定阅的设备did1）
     * {"did": <str> },（需要定阅的设备did2）
     * ……
     * ]
     * }
     */
    private void subscribeDevice(List<DeviceInfo> deviceList) {
        if (deviceList == null) {
            return;
        }
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(Constants.Key.CMD, Constants.Value.SUBSCRIBE_REQ);
            JSONArray jsonArray = new JSONArray();

            for (DeviceInfo info : deviceList) {
                JSONObject deviceObject = new JSONObject();
                deviceObject.put(Constants.Key.DID, info.did);
                jsonArray.add(deviceObject);
            }
            jsonObject.put(Constants.Key.DATA, jsonArray);
            sendMessage(jsonObject.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取设备在线状态
     *
     * @param mac 指定mac
     * @return 0-设备不存在|1-在线|2-离线
     */
    public int getOnOffStatus(String mac) {
        if (deviceStatus.size() == 0) {
            return 0;
        }
        for (DeviceStatus value : deviceStatus.values()) {
            if (mac.equalsIgnoreCase(value.mac)) {
                return value.onOffStatus ? 1 : 2;
            }
        }

        return 0;
    }

    /**
     * <p>标准数据点读操作
     * <p>{
     * <p>    "cmd": "c2s_read",
     * <p>    "req_sn": <int>, (可选参数,若有值下发指令为带ACK的业务透传指令 [0x0093],若无则为不带ACK的业务
     * 透传指令 [0x0090])
     * <p>    "data":
     * <p>    {
     * <p>        "did": <str>,（目标设备的did）
     * <p>        "names": [<str>, <str>, …] （变长数据点可选参数：传入需要读取的数据点名称，
     * 参数省略表示读取全部数据点；定长数据点读操作忽略该参数）
     * <p>    }
     * <p>}
     *
     * @param mac   mac地址，不为空
     * @param did   机智云设备did
     * @param value 需要读取的数据点
     */
    public void c2sRead(String mac, String did, String value) {
        if (TextUtils.isEmpty(mac)) {
            Logc.d(TAG, "c2sRead mac地址不能为null");
            // ToastUtils.getInstance().showCustomToast("c2sRead mac地址不能为null");
            return;
        }
        if (TextUtils.isEmpty(did)) {
            // 获取did
            for (DeviceStatus status : deviceStatus.values()) {
                if (mac.equalsIgnoreCase(status.mac)) {
                    did = status.did;
                    break;
                }
            }
            if (TextUtils.isEmpty(did)) {
                Logc.d(TAG, "c2sRead 没有找到设备did");
                return;
            }
        }
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(Constants.Key.CMD, Constants.Value.C_2_S_READ);
            // 可选参数,若有值下发指令为带ACK的业务透传指令 [0x0093],若无则为不带ACK的业务透传指令 [0x0090]
            // jsonObject.put(Constants.Key.REQ_SN, new Random().nextInt());
            JSONObject data = new JSONObject();
            data.put(Constants.Key.DID, did);
            data.put(Constants.Key.NAMES, JSONObject.parse(value));
            jsonObject.put(Constants.Key.DATA, data);
            sendMessage(jsonObject.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * <p>标准数据点读操作
     * <p>{
     * <p>    "cmd": "c2s_write",
     * <p>    "req_sn": <int>, (可选参数,若有值下发指令为带ACK的业务透传指令 [0x0093],若无则为不带ACK的业务
     * 透传指令 [0x0090])
     * <p>    "data":
     * <p>    {
     * <p>        "did": <str>,（目标设备的did）
     * <p>        "attrs":
     * {
     * <p>            "name1": <value1>, (“name1”指数据点的标识名(name)，<value1>指数据点的值。
     * 值可以为true/false(布尔型)，Unicode编码的字符串如\u62bd(枚举型)，数字（数值型），byte数组[23,2,3]（扩展类型)
     * <p>            "name2": <value2>,
     * <p>            ...
     * <p>        }
     * <p>    }
     * <p>}
     *
     * @param mac   mac地址，不为空
     * @param did   机智云设备did
     * @param value 需要读取的数据点
     */
    public boolean c2sWrite(String mac, String did, String value) {
        if (TextUtils.isEmpty(mac)) {
            Logc.d(TAG, "c2sWrite mac地址不能为null");
            // ToastUtils.getInstance().showCustomToast("c2sWrite mac地址不能为null");
            return false;
        }
        if (TextUtils.isEmpty(did)) {
            // 获取did
            for (DeviceStatus status : deviceStatus.values()) {
                if (mac.equalsIgnoreCase(status.mac)) {
                    did = status.did;
                    break;
                }
            }
            if (TextUtils.isEmpty(did)) {
                Logc.d(TAG, "c2sWrite 没有找到设备did");
                return false;
            }
        }
        Logc.d(TAG, "value: " + value);
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(Constants.Key.CMD, Constants.Value.C_2_S_WRITE);
            // 可选参数,若有值下发指令为带ACK的业务透传指令 [0x0093],若无则为不带ACK的业务透传指令 [0x0090]
            // jsonObject.put(Constants.Key.REQ_SN, new Random().nextInt());
            JSONObject data = new JSONObject();
            data.put(Constants.Key.DID, did);
            data.put(Constants.Key.ATTRS, JSONObject.parseObject(value));
            jsonObject.put(Constants.Key.DATA, data);
            sendMessage(jsonObject.toString());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * <p>浏览器 ⇒ 云端。浏览器收到带有req_sn的s2c_noti消息后 [0x0093],需要浏览器向云端发送以下 的JSON字符串[0x0094]
     * <p>{
     * <p>    "cmd": "c2s_ack",
     * <p>    "res_sn": <int>, (对应浏览器接收到s2c_noti指令的req_sn)
     * <p>    "did": <str>(目标设备did)
     * <p>}
     *
     * @param sn  对应浏览器接收到s2c_noti指令的req_sn
     * @param did 机智云设备did
     */
    public void c2sAck(int sn, String did) {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(Constants.Key.CMD, Constants.Value.C_2_S_ACK);
            // 可选参数,若有值下发指令为带ACK的业务透传指令 [0x0093],若无则为不带ACK的业务透传指令 [0x0090]
            // jsonObject.put(Constants.Key.REQ_SN, new Random().nextInt());
            jsonObject.put(Constants.Key.DID, did);
            jsonObject.put(Constants.Key.RES_SN, sn);
            sendMessage(jsonObject.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void exit() {
        isExit = true;
        WsManager.getInstance().logout();
        Logc.i(TAG, "exit: logout");
        if (future != null) {
            future.cancel(false);
        }
        loginStr = null;
    }

    /**
     * 发送消息
     *
     * @param message
     */
    public void sendMessage(String message) {
        WsManager.getInstance().sendMessage(message);
    }

    /**
     * 发送心跳包
     */
    public void sendHeartPackage() {
        WsManager.getInstance().sendHeartPackage(getHeardBeatStr());
    }

    /**
     * 心跳包
     *
     * @return
     */
    private String getHeardBeatStr() {
        if (TextUtils.isEmpty(heardBeatStr)) {
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put(Constants.Key.CMD, Constants.Value.PING);
                heardBeatStr = jsonObject.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return heardBeatStr;
    }

    /**
     * 心跳间隔，默认60s
     */
    private static final long HEART_BEAT_RATE = 60;

    private Runnable heartBeatRunnable = new Runnable() {
        @Override
        public void run() {
            if (!WsHelper.getInstance().isNullConnectionWebsocket()) {
                if (WsHelper.getInstance().isCloseConnectionWebsocket()) {
                    Logc.d(TAG, "ws重连--------");
                    reconnectWs();
                } else {
                    //发送心跳包
                    Logc.i(TAG, "ws开始发送心跳包");
                    WsHelper.getInstance().sendHeartPackage();
                }
            } else {
                //如果client已为空，重新初始化websocket
                Logc.d(TAG, "重新初始化WebSocket");
                init(context);
            }
            //定时对长连接进行心跳检测
            future = ThreadUtils.getScheduledExecutorService().schedule(this, HEART_BEAT_RATE,
                    TimeUnit.SECONDS);
        }
    };


    /**
     * 开启重连
     */
    private void reconnectWs() {
        if (future != null) {
            future.cancel(false);
        }
        ThreadUtils.execute(() -> {
            try {
                //尝试重连
                WsHelper.getInstance().reConnectionWebsocket();
            } catch (Exception e) {
                e.printStackTrace();
                Logc.i(TAG, "ws重连失败" + e.getMessage());
            }
        });
    }
}
